#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Shopify Webhook服务
负责验证和处理来自Shopify平台的Webhook事件通知
"""

import hmac
import hashlib
import time
from typing import Dict, Any, Optional, Callable, List
import json
from datetime import datetime

from app.conf.config import config, logger
from app.utils.db_utils import db


class ShopifyWebhookService:
    """
    Shopify Webhook服务类
    提供Webhook验证、处理和管理功能
    """
    
    # 支持的Webhook主题列表
    SUPPORTED_TOPICS = [
        # 店铺相关
        'shop/update',
        'app/uninstalled',
        # 产品相关
        'products/create',
        'products/update',
        'products/delete',
        # 订单相关
        'orders/create',
        'orders/updated',
        'orders/cancelled',
        'orders/fulfilled',
        # 客户相关
        'customers/create',
        'customers/update',
        'customers/delete',
        # 库存相关
        'inventory_levels/connect',
        'inventory_levels/update',
        # 集合相关
        'collections/create',
        'collections/update',
        'collections/delete'
    ]
    
    def __init__(self):
        """
        初始化Webhook服务
        """
        self.db = db
        self.logger = logger
        self.secret_key = config.SHOPIFY_API_SECRET
        self._handlers: Dict[str, List[Callable]] = {}
    
    def verify_webhook(self, hmac_header: str, request_body: bytes, topic: str = None) -> bool:
        """
        验证Webhook请求的合法性
        使用HMAC SHA256算法验证请求签名
        
        Args:
            hmac_header: 请求头中的X-Shopify-Hmac-SHA256值
            request_body: 请求体原始字节内容
            topic: Webhook主题（可选）
            
        Returns:
            bool: 验证是否成功
        """
        try:
            # 计算签名
            digest = hmac.new(
                self.secret_key.encode('utf-8'),
                request_body,
                digestmod=hashlib.sha256
            ).digest()
            
            # 将签名转换为十六进制字符串
            calculated_hmac = digest.hex()
            
            # 比较计算出的签名和请求头中的签名
            # 使用时间安全的比较方法
            is_valid = hmac.compare_digest(calculated_hmac, hmac_header)
            
            if is_valid:
                self.logger.debug(f"Webhook验证成功: {topic}")
            else:
                self.logger.warning(f"Webhook验证失败: 签名不匹配")
            
            return is_valid
            
        except Exception as e:
            self.logger.error(f"Webhook验证异常: {str(e)}")
            return False
    
    def save_webhook_log(self, shop_domain: str, topic: str, webhook_id: str,
                        request_id: str, payload: str, status: str = 'received',
                        error_message: str = None) -> int:
        """
        保存Webhook日志到数据库
        
        Args:
            shop_domain: 店铺域名
            topic: Webhook主题
            webhook_id: Shopify Webhook ID
            request_id: 请求ID
            payload: Webhook原始数据
            status: 处理状态
            error_message: 错误信息
            
        Returns:
            int: 日志记录的ID
        """
        try:
            # 限制payload长度，避免数据库过大
            max_payload_length = 100000  # 100KB
            if payload and len(payload) > max_payload_length:
                payload = payload[:max_payload_length] + "... [内容过长已截断]"
            
            # 插入日志记录
            result = self.db.execute_sql("""
                INSERT INTO shopify_webhook_logs 
                (shop_domain, topic, webhook_id, request_id, payload, status, error_message)
                VALUES (%s, %s, %s, %s, %s, %s, %s)
            """, {
                'shop_domain': shop_domain,
                'topic': topic,
                'webhook_id': webhook_id,
                'request_id': request_id,
                'payload': payload,
                'status': status,
                'error_message': error_message
            })
            
            # 获取插入的记录ID
            log_id = self.db.execute_sql("SELECT LAST_INSERT_ID()")[0]['LAST_INSERT_ID()']
            self.logger.debug(f"Webhook日志已保存，ID: {log_id}, 店铺: {shop_domain}, 主题: {topic}")
            
            return log_id
            
        except Exception as e:
            self.logger.error(f"保存Webhook日志失败: {str(e)}")
            return 0
    
    def update_webhook_status(self, log_id: int, status: str, 
                            error_message: str = None, processing_time: int = None):
        """
        更新Webhook处理状态
        
        Args:
            log_id: 日志记录ID
            status: 新状态
            error_message: 错误信息
            processing_time: 处理耗时（毫秒）
        """
        try:
            update_data = {'status': status}
            if error_message is not None:
                update_data['error_message'] = error_message
            if processing_time is not None:
                update_data['processing_time'] = processing_time
            
            self.db.execute_sql("""
                UPDATE shopify_webhook_logs 
                SET status = %(status)s
                {error_message_part}
                {processing_time_part}
                WHERE id = %(log_id)s
            """.format(
                error_message_part=", error_message = %(error_message)s" if error_message is not None else "",
                processing_time_part=", processing_time = %(processing_time)s" if processing_time is not None else ""
            ), {
                **update_data,
                'log_id': log_id
            })
            
            self.logger.debug(f"Webhook状态已更新，ID: {log_id}, 状态: {status}")
            
        except Exception as e:
            self.logger.error(f"更新Webhook状态失败 (ID: {log_id}): {str(e)}")
    
    def register_webhook(self, shop_domain: str, topic: str, address: str, 
                        format: str = 'json') -> Dict[str, Any]:
        """
        注册店铺Webhook配置到数据库
        
        Args:
            shop_domain: 店铺域名
            topic: Webhook主题
            address: Webhook回调URL
            format: 数据格式
            
        Returns:
            Dict: 注册结果
        """
        try:
            # 获取店铺ID
            store_result = self.db.execute_sql("""
                SELECT id FROM shopify_stores WHERE shop_domain = %(shop_domain)s
            """, {'shop_domain': shop_domain})
            
            if not store_result:
                return {'success': False, 'error': '店铺不存在'}
            
            shopify_store_id = store_result[0]['id']
            
            # 检查是否已存在该主题的Webhook
            existing = self.db.execute_sql("""
                SELECT id, webhook_id FROM shopify_webhook_configs 
                WHERE shopify_store_id = %(shopify_store_id)s AND topic = %(topic)s
            """, {
                'shopify_store_id': shopify_store_id,
                'topic': topic
            })
            
            if existing:
                # 更新现有Webhook
                self.db.execute_sql("""
                    UPDATE shopify_webhook_configs 
                    SET address = %(address)s, format = %(format)s, is_active = 1
                    WHERE id = %(id)s
                """, {
                    'id': existing[0]['id'],
                    'address': address,
                    'format': format
                })
                
                return {
                    'success': True,
                    'action': 'updated',
                    'webhook_config_id': existing[0]['id'],
                    'shopify_webhook_id': existing[0]['webhook_id']
                }
            else:
                # 创建新的Webhook配置
                self.db.execute_sql("""
                    INSERT INTO shopify_webhook_configs 
                    (shopify_store_id, topic, address, format, is_active)
                    VALUES (%(shopify_store_id)s, %(topic)s, %(address)s, %(format)s, 1)
                """, {
                    'shopify_store_id': shopify_store_id,
                    'topic': topic,
                    'address': address,
                    'format': format
                })
                
                config_id = self.db.execute_sql("SELECT LAST_INSERT_ID()")[0]['LAST_INSERT_ID()']
                
                return {
                    'success': True,
                    'action': 'created',
                    'webhook_config_id': config_id
                }
                
        except Exception as e:
            self.logger.error(f"注册Webhook配置失败: {str(e)}")
            return {'success': False, 'error': str(e)}
    
    def update_shopify_webhook_id(self, config_id: int, webhook_id: str) -> bool:
        """
        更新Webhook配置中的Shopify Webhook ID
        
        Args:
            config_id: Webhook配置ID
            webhook_id: Shopify返回的Webhook ID
            
        Returns:
            bool: 更新是否成功
        """
        try:
            self.db.execute_sql("""
                UPDATE shopify_webhook_configs 
                SET webhook_id = %(webhook_id)s
                WHERE id = %(config_id)s
            """, {
                'config_id': config_id,
                'webhook_id': webhook_id
            })
            
            return True
            
        except Exception as e:
            self.logger.error(f"更新Shopify Webhook ID失败: {str(e)}")
            return False
    
    def handle_webhook(self, shop_domain: str, topic: str, request_id: str, 
                      payload_json: str, hmac_header: str) -> Dict[str, Any]:
        """
        处理接收到的Webhook事件
        
        Args:
            shop_domain: 店铺域名
            topic: Webhook主题
            request_id: 请求ID
            payload_json: Webhook数据(JSON字符串)
            hmac_header: HMAC签名
            
        Returns:
            Dict: 处理结果
        """
        start_time = time.time()
        result = {
            'success': False,
            'status': 'failed',
            'log_id': 0,
            'message': '处理失败'
        }
        
        try:
            # 验证Webhook签名
            if not self.verify_webhook(hmac_header, payload_json.encode('utf-8'), topic):
                result['message'] = 'Webhook签名验证失败'
                return result
            
            # 验证主题是否支持
            if topic not in self.SUPPORTED_TOPICS:
                self.logger.warning(f"收到不支持的Webhook主题: {topic}")
                # 仍然记录日志，但不处理
            
            # 解析JSON数据
            payload = json.loads(payload_json)
            webhook_id = payload.get('id')  # 某些Webhook可能包含id字段
            
            # 保存Webhook日志
            log_id = self.save_webhook_log(
                shop_domain=shop_domain,
                topic=topic,
                webhook_id=webhook_id,
                request_id=request_id,
                payload=payload_json,
                status='processing'
            )
            result['log_id'] = log_id
            
            # 执行注册的处理程序
            self._execute_handlers(topic, shop_domain, payload, log_id)
            
            # 处理特殊事件
            if topic == 'app/uninstalled':
                self._handle_app_uninstalled(shop_domain)
            
            # 计算处理时间
            processing_time = int((time.time() - start_time) * 1000)
            
            # 更新Webhook状态为成功
            self.update_webhook_status(
                log_id=log_id,
                status='completed',
                processing_time=processing_time
            )
            
            result.update({
                'success': True,
                'status': 'completed',
                'message': 'Webhook处理成功',
                'processing_time': processing_time
            })
            
            self.logger.info(f"Webhook处理成功: {topic}, 店铺: {shop_domain}, 耗时: {processing_time}ms")
            
        except json.JSONDecodeError as e:
            error_msg = f"JSON解析错误: {str(e)}"
            self.logger.error(error_msg)
            if result['log_id']:
                self.update_webhook_status(
                    log_id=result['log_id'],
                    status='failed',
                    error_message=error_msg
                )
            result['message'] = error_msg
            
        except Exception as e:
            error_msg = f"处理异常: {str(e)}"
            self.logger.error(error_msg)
            if result['log_id']:
                self.update_webhook_status(
                    log_id=result['log_id'],
                    status='failed',
                    error_message=error_msg
                )
            result['message'] = error_msg
        
        return result
    
    def register_handler(self, topic: str, handler: Callable):
        """
        注册Webhook事件处理函数
        
        Args:
            topic: Webhook主题
            handler: 处理函数，接受参数(shop_domain: str, payload: Dict, log_id: int)
        """
        if topic not in self._handlers:
            self._handlers[topic] = []
        
        self._handlers[topic].append(handler)
        self.logger.debug(f"已注册Webhook处理函数: {topic}, 处理函数数量: {len(self._handlers[topic])}")
    
    def _execute_handlers(self, topic: str, shop_domain: str, payload: Dict, log_id: int):
        """
        执行注册的Webhook处理函数
        
        Args:
            topic: Webhook主题
            shop_domain: 店铺域名
            payload: Webhook数据
            log_id: 日志ID
        """
        handlers = self._handlers.get(topic, [])
        
        for handler in handlers:
            try:
                handler(shop_domain, payload, log_id)
                self.logger.debug(f"Webhook处理函数执行成功: {handler.__name__}, 主题: {topic}")
            except Exception as e:
                self.logger.error(f"Webhook处理函数执行失败: {handler.__name__}, 错误: {str(e)}")
    
    def _handle_app_uninstalled(self, shop_domain: str):
        """
        处理应用卸载事件
        
        Args:
            shop_domain: 店铺域名
        """
        try:
            # 更新店铺状态为已卸载
            now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            self.db.execute_sql("""
                UPDATE shopify_stores 
                SET is_active = 0, uninstalled_at = %s
                WHERE shop_domain = %s
            """, [now, shop_domain])
            
            # 记录安装日志
            store_result = self.db.execute_sql("""
                SELECT id FROM shopify_stores WHERE shop_domain = %s
            """, [shop_domain])
            
            if store_result:
                shopify_store_id = store_result[0]['id']
                self.db.execute_sql("""
                    INSERT INTO shopify_installation_logs 
                    (shopify_store_id, shop_domain, event_type, reason)
                    VALUES (%s, %s, 'uninstall', 'App uninstalled by merchant')
                """, [shopify_store_id, shop_domain, 'uninstall', 'App uninstalled by merchant'])
            
            self.logger.info(f"应用已被卸载: {shop_domain}")
            
        except Exception as e:
            self.logger.error(f"处理应用卸载事件失败: {str(e)}")
    
    def get_webhook_stats(self, shop_domain: str = None, days: int = 7) -> Dict[str, Any]:
        """
        获取Webhook统计信息
        
        Args:
            shop_domain: 店铺域名（可选）
            days: 统计天数
            
        Returns:
            Dict: 统计数据
        """
        try:
            where_clause = ""
            params = {'days': days}
            
            if shop_domain:
                where_clause = "AND shop_domain = %(shop_domain)s"
                params['shop_domain'] = shop_domain
            
            # 获取按主题统计的数据
            stats = self.db.execute_sql("""
                SELECT 
                    topic,
                    COUNT(*) as total_count,
                    SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as success_count,
                    SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_count,
                    AVG(processing_time) as avg_processing_time
                FROM 
                    shopify_webhook_logs
                WHERE 
                    created_at >= DATE_SUB(NOW(), INTERVAL %(days)s DAY)
                    {where_clause}
                GROUP BY 
                    topic
                ORDER BY 
                    total_count DESC
            """.format(where_clause=where_clause), params)
            
            # 获取总体统计
            overall = self.db.execute_sql("""
                SELECT 
                    COUNT(*) as total_count,
                    SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as success_count,
                    SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_count,
                    AVG(processing_time) as avg_processing_time
                FROM 
                    shopify_webhook_logs
                WHERE 
                    created_at >= DATE_SUB(NOW(), INTERVAL %(days)s DAY)
                    {where_clause}
            """.format(where_clause=where_clause), params)
            
            return {
                'overall': overall[0] if overall else {},
                'by_topic': stats,
                'days': days,
                'shop_domain': shop_domain
            }
            
        except Exception as e:
            self.logger.error(f"获取Webhook统计失败: {str(e)}")
            return {
                'overall': {},
                'by_topic': [],
                'error': str(e)
            }


# 初始化Webhook服务实例
shopify_webhook_service = ShopifyWebhookService()